package com.zenithmind.article.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zenithmind.article.feign.UserFeignClient;
import com.zenithmind.article.mapper.ArticleMapper;
import com.zenithmind.article.mapper.ArticleTagMapper;
import com.zenithmind.article.mapper.CategoryMapper;
import com.zenithmind.article.mapper.TagMapper;
import com.zenithmind.article.pojo.domain.*;
import com.zenithmind.article.pojo.dto.ArticleTagDTO;
import com.zenithmind.article.pojo.query.ArticleQuery;
import com.zenithmind.article.pojo.vo.ArticleVO;
import com.zenithmind.article.pojo.vo.AuthorVO;
import com.zenithmind.article.pojo.vo.UserVO;
import com.zenithmind.article.service.ArticleService;
import com.zenithmind.article.service.ArticleTagService;
import com.zenithmind.common.result.PageResult;
import com.zenithmind.common.result.Result;
import com.zenithmind.common.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.cache.annotation.Caching;
import com.zenithmind.common.constant.CacheConstants;
import com.zenithmind.common.exception.BusinessException;
import com.zenithmind.common.exception.NotFoundException;
import com.zenithmind.common.annotation.DataCache;
import com.zenithmind.common.annotation.DataCacheEvict;
import com.zenithmind.common.security.SecurityUtils;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
    @Autowired
    private ArticleTagMapper articleTagMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private ArticleTagService articleTagService;

    @Autowired
    private UserFeignClient userFeignClient;
    
    @Autowired
    private TagMapper tagMapper;
    
    @Value("${article.upload.path:${user.home}/zenithmind/upload/articles}")
    private String uploadPath;
    
    @Value("${article.upload.url-prefix:http://localhost:8083/articles}")
    private String urlPrefix;
    
    @Autowired
    private RedisUtils redisUtils;

    private static final long MAX_COVER_SIZE = 5 * 1024 * 1024; // 5MB limit example
    private static final List<String> ALLOWED_COVER_TYPES = Arrays.asList("image/jpeg", "image/png", "image/gif");

    // 添加缓存前缀常量
    private static final String CACHE_ARTICLE_PREFIX = "article";
    private static final String CACHE_ARTICLE_LIST_PREFIX = "article:list";
    private static final String CACHE_HOT_ARTICLES_KEY = "hot";
    private static final String CACHE_ARTICLE_ARCHIVES_KEY = "archives";

    @Override
    public String uploadCover(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException("上传文件不能为空");
        }

        // Validate file size
        if (file.getSize() > MAX_COVER_SIZE) {
            throw new BusinessException("文件大小不能超过 " + (MAX_COVER_SIZE / 1024 / 1024) + "MB");
        }

        // Validate file type
        String contentType = file.getContentType();
        if (contentType == null || !ALLOWED_COVER_TYPES.contains(contentType.toLowerCase())) {
             throw new BusinessException("不支持的文件类型，请上传 JPG, PNG, 或 GIF 图片");
        }

        String fileName = file.getOriginalFilename();
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        String newFileName = UUID.randomUUID() + suffix;
        String filePath = uploadPath + File.separator;
        File dest = new File(filePath + newFileName);
        if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdirs();
        }
        try {
            file.transferTo(dest);
        } catch (IOException e) {
            throw new RuntimeException("文件上传失败");
        }
        return urlPrefix + "/" + newFileName;
    }

    @Override
    @Transactional
    @DataCacheEvict(prefix = CACHE_ARTICLE_PREFIX, key = "#article.id")
    public Article addOrUpdateArticle(Article article) {
        log.info("新增或更新文章: {}", article.getTitle());
        if (article.getId() == null) {
            // 新增文章，设置作者信息
            String userId = SecurityUtils.getCurrentUserId();
            article.setUserId(userId);
        }
        updateById(article);
        return article;
    }

    @Override
    @DataCache(prefix = CACHE_ARTICLE_LIST_PREFIX, key = "'page' + #articleQuery.current", 
              expireTime = CacheConstants.TTL_SECONDS_DEFAULT)
    public PageResult<ArticleVO> pageArticle(ArticleQuery articleQuery) {
        Page<Article> page = articleQuery.toPage();
        // 1. Query base Article page
        Page<Article> articlePage = lambdaQuery().like(StringUtils.isNotBlank(articleQuery.getTitle()), Article::getTitle, articleQuery.getTitle())
                .eq(articleQuery.getStatus() != null, Article::getStatus, articleQuery.getStatus())
                .eq(StringUtils.isNotBlank(articleQuery.getCategoryId()), Article::getCategoryId, articleQuery.getCategoryId())
                .eq(StringUtils.isNotBlank(articleQuery.getUserId()), Article::getUserId, articleQuery.getUserId())
                .eq(articleQuery.getIsTop() != null, Article::getIsTop, articleQuery.getIsTop())
                .eq(articleQuery.getIsRecommend() != null, Article::getIsRecommend, articleQuery.getIsRecommend())
                .orderByDesc(Article::getCreateTime) // Add ordering if needed
                .page(page);

        List<Article> articles = articlePage.getRecords();
        if (articles.isEmpty()) {
            return new PageResult<>(Collections.emptyList(), articlePage.getTotal(), articlePage.getCurrent(), articlePage.getSize());
        }

        // 2. Extract IDs for batch fetching
        List<String> articleIds = articles.stream().map(Article::getId).collect(Collectors.toList());
        List<String> userIds = articles.stream().map(Article::getUserId).distinct().collect(Collectors.toList());
        List<String> categoryIds = articles.stream().map(Article::getCategoryId).distinct().collect(Collectors.toList());

        // 3. Batch fetch related data
        // Fetch Users (relies on user-service cache or potentially a batch endpoint)
        Map<String, UserVO> userMap = userIds.stream()
                                             .map(userId -> {
                                                 try {
                                                     Result<UserVO> userResult = userFeignClient.getUserById(userId);
                                                     return (userResult != null && userResult.isSuccess()) ? userResult.getData() : null;
                                                 } catch (Exception e) {
                                                     log.error("Error fetching user {} via Feign: {}", userId, e.getMessage());
                                                     return null;
                                                 }
                                             })
                                             .filter(Objects::nonNull)
                                             .collect(Collectors.toMap(UserVO::getId, Function.identity(), (u1, u2) -> u1)); // Handle potential duplicates

        // Fetch Categories
        Map<String, Category> categoryMap = categoryIds.isEmpty() ? Collections.emptyMap() :
                                            categoryMapper.selectBatchIds(categoryIds).stream()
                                                        .collect(Collectors.toMap(Category::getId, Function.identity()));

        // Fetch Tags (using the existing mapper method efficiently)
        Map<String, List<Tag>> tagsMap = articleTagMapper.selectList(new LambdaQueryWrapper<ArticleTag>()
                                            .in(ArticleTag::getArticleId, articleIds))
                                            .stream()
                                            .collect(Collectors.groupingBy(
                                                    ArticleTag::getArticleId,
                                                    Collectors.mapping(articleTag -> 
                                                        tagMapper.selectById(articleTag.getTagId()),
                                                    Collectors.toList())));

        // 4. Assemble ArticleVOs using fetched data
        List<ArticleVO> articleVOs = articles.stream()
                .map(article -> {
                    ArticleVO articleVO = new ArticleVO();
                    BeanUtils.copyProperties(article, articleVO);

                    // Set data from maps
                    articleVO.setUserVo(userMap.get(article.getUserId())); // Can be null if fetch failed
                    articleVO.setCategory(categoryMap.get(article.getCategoryId()));
                    articleVO.setTag(tagsMap.getOrDefault(article.getId(), Collections.emptyList()));

                    return articleVO;
                }).collect(Collectors.toList());

        return new PageResult<>(articleVOs, articlePage.getTotal(), articlePage.getCurrent(), articlePage.getSize());
    }

    @Override
    @Transactional
    @DataCacheEvict(prefix = CACHE_ARTICLE_PREFIX, key = "#articleTagDTO.articleId")
    public ArticleVO bindTags(ArticleTagDTO articleTagDTO) {
        log.info("为文章 {} 绑定标签", articleTagDTO.getArticleId());
        
        // 检查文章ID是否为空
        if (StringUtils.isBlank(articleTagDTO.getArticleId())) {
            throw new RuntimeException("文章id不能为空");
        }

        //遍历文章标签列表并插入数据库
        for (String tagId : articleTagDTO.getTagId()) {
            ArticleTag articleTag = new ArticleTag();
            articleTag.setArticleId(articleTagDTO.getArticleId());
            articleTag.setTagId(tagId);
            articleTagService.save(articleTag);
            log.info("给文章 {} 绑定标签 {}", articleTagDTO.getArticleId(), tagId);
        }
        // MyBatis-Plus进行连表查询: 文章标签关联表 - 文章表 - 标签表,最后返回文章和标签信息
        List<Tag> articleTags = articleTagMapper.selectTagsArticleTag(articleTagDTO.getArticleId());
        if (articleTags == null || articleTags.isEmpty()) {
            throw new RuntimeException("未找到相关文章或标签信息");
        }

        // 获取第一条记录（因为文章信息是相同的，只是标签不同）
        Tag firstRecord = articleTags.get(0);
        ArticleVO articleVO = new ArticleVO();

        // 设置文章信息
       BeanUtils.copyProperties(firstRecord, articleVO);

        // 收集所有标签名称
        articleVO.setTag(articleTags);

        return articleVO;
    }

    @Override
    @DataCache(prefix = CACHE_ARTICLE_PREFIX, key = "#id", expireTime = CacheConstants.TTL_SECONDS_ARTICLE)
    public ArticleVO getArticleById(String id) {
        log.info("从数据库获取文章: {}", id);
        
        // 查询文章基本信息
        Article article = getById(id);
        if (article == null) {
            throw new RuntimeException("文章不存在");
        }

        // 转换为ArticleVO
        ArticleVO articleVO = new ArticleVO();
        BeanUtils.copyProperties(article, articleVO);

        // 查询文章作者
        Result<UserVO> userResult = userFeignClient.getUserById(article.getUserId());
        if (userResult != null && userResult.isSuccess() && userResult.getData() != null) {
            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(userResult.getData(), userVO);
            articleVO.setUserVo(userVO);
        } else {
            log.error("Failed to get user info for article: {}. Reason: {} - {}", 
                      id, 
                      userResult != null ? userResult.getCode() : "NULL_RESULT", 
                      userResult != null ? userResult.getMsg() : "Feign client returned null");
            articleVO.setUserVo(null);
        }

        // 查询文章分类
        Category category = categoryMapper.selectById(article.getCategoryId());
        articleVO.setCategory(category);

        // 查询文章的标签信息
        List<Tag> articleTags = articleTagMapper.selectTagsArticleTag(id);
        articleVO.setTag(articleTags);

        return articleVO;
    }

    @Override
    @Transactional
    @DataCacheEvict(prefix = CACHE_ARTICLE_PREFIX, key = "#articleId")
    public ArticleVO addCategory(String articleId, String categoryId) {
        log.info("为文章 {} 添加分类 {}", articleId, categoryId);
        
        if (StringUtils.isBlank(articleId)) {
            throw new RuntimeException("文章id不能为空");
        }
        if (StringUtils.isBlank(categoryId)) {
            throw new RuntimeException("分类id不能为空");
        }
        Article article = getById(articleId);
        if (article == null) {
            throw new RuntimeException("文章不存在: " + articleId);
        }
        article.setCategoryId(categoryId);
        updateById(article);
        return getArticleById(articleId);
    }

    @Override
    @DataCache(prefix = CACHE_ARTICLE_LIST_PREFIX, key = CACHE_HOT_ARTICLES_KEY, 
              expireTime = CacheConstants.TTL_SECONDS_DEFAULT)
    public List<ArticleVO> getHotArticles() {
        // 获取热门文章（按浏览量排序）
        List<Article> hotArticles = lambdaQuery()
                .eq(Article::getStatus, 1)  // 已发布状态
                .orderByDesc(Article::getViewCount)
                .last("LIMIT 10")
                .list();
        
        return hotArticles.stream().map(this::convertToVO).collect(Collectors.toList());
    }
    
    @Override
    @DataCache(prefix = CACHE_ARTICLE_LIST_PREFIX, key = "'hot:blog:' + #limit", 
              expireTime = CacheConstants.TTL_SECONDS_DEFAULT)
    public List<ArticleVO> getBlogHotArticles(Integer limit) {
        // 获取博客页面热门文章（按浏览量和点赞数排序）
        List<Article> hotArticles = lambdaQuery()
                .eq(Article::getStatus, 1)  // 已发布状态
                .orderByDesc(Article::getViewCount)
                .orderByDesc(Article::getLikeCount)
                .last("LIMIT " + limit)
                .list();
        
        return hotArticles.stream()
                .map(article -> {
                    ArticleVO vo = new ArticleVO();
                    BeanUtils.copyProperties(article, vo);
                    return vo;
                })
                .collect(Collectors.toList());
    }
    
    @Override
    @DataCache(prefix = CACHE_ARTICLE_LIST_PREFIX, key = "'authors:active:' + #limit", 
              expireTime = CacheConstants.TTL_SECONDS_DEFAULT)
    public List<AuthorVO> getActiveAuthors(Integer limit) {
        // 获取活跃作者（按文章数量和总阅读量排序）
        List<Map<String, Object>> authorStats = baseMapper.getActiveAuthors(limit);
        
        if (authorStats.isEmpty()) {
            return new ArrayList<>();
        }
        
        // 获取用户详情
        List<AuthorVO> authors = new ArrayList<>();
        for (Map<String, Object> stat : authorStats) {
            String userId = (String) stat.get("userId");
            Integer articleCount = ((Number) stat.get("articleCount")).intValue();
            Integer totalViews = ((Number) stat.get("totalViews")).intValue();
            
            try {
                Result<UserVO> userResult = userFeignClient.getUserById(userId);
                if (userResult != null && userResult.isSuccess() && userResult.getData() != null) {
                    UserVO user = userResult.getData();
                    
                    AuthorVO author = new AuthorVO();
                    author.setId(userId);
                    author.setName(user.getUsername());
                    author.setAvatar(user.getAvatar());
                    author.setArticleCount(articleCount);
                    author.setTotalViews(totalViews);
                    author.setIntroduction(user.getIntroduction());
                    
                    authors.add(author);
                }
            } catch (Exception e) {
                log.error("Error fetching user {} via Feign: {}", userId, e.getMessage());
            }
        }
        
        return authors;
    }
    
    @Override
    @DataCache(prefix = CACHE_ARTICLE_LIST_PREFIX, key = CACHE_ARTICLE_ARCHIVES_KEY, 
              expireTime = CacheConstants.TTL_SECONDS_DEFAULT)
    public List<ArticleVO> getArticleArchives() {
        log.info("从数据库获取文章归档");
        
        // 使用 MyBatis Plus 的方式查询文章归档
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Article::getStatus, 1) // 已发布的文章
                .orderByDesc(Article::getCreateTime);
        List<Article> articles = list(queryWrapper);
        
        // 转换为VO
        return articles.stream()
                .map(article -> {
                    ArticleVO vo = new ArticleVO();
                    BeanUtils.copyProperties(article, vo);
                    return vo;
                })
                .collect(Collectors.toList());
    }
    
    @Override
    @DataCache(prefix = CACHE_ARTICLE_LIST_PREFIX, key = "'related:' + #articleId", 
              expireTime = CacheConstants.TTL_SECONDS_DEFAULT)
    public List<ArticleVO> getRelatedArticles(String articleId) {
        log.info("从数据库获取文章 {} 的相关文章", articleId);
        
        // 获取当前文章
        Article article = getById(articleId);
        if (article == null) {
            return new ArrayList<>();
        }
        
        // 使用 MyBatis Plus 的方式查询相同分类的文章
        LambdaQueryWrapper<Article> categoryWrapper = new LambdaQueryWrapper<>();
        categoryWrapper.eq(Article::getCategoryId, article.getCategoryId())
                .eq(Article::getStatus, 1) // 已发布的文章
                .ne(Article::getId, articleId) // 排除当前文章
                .orderByDesc(Article::getCreateTime)
                .last("LIMIT 5");
        List<Article> sameCategoryArticles = list(categoryWrapper);
        
        // 使用 MyBatis Plus 的方式查询相同标签的文章
        // 首先获取文章的标签ID列表
        LambdaQueryWrapper<ArticleTag> tagWrapper = new LambdaQueryWrapper<>();
        tagWrapper.eq(ArticleTag::getArticleId, articleId);
        List<ArticleTag> articleTags = articleTagMapper.selectList(tagWrapper);
        
        List<Article> sameTagArticles = new ArrayList<>();
        if (!articleTags.isEmpty()) {
            // 获取标签ID列表
            List<String> tagIds = articleTags.stream()
                    .map(ArticleTag::getTagId)
                    .collect(Collectors.toList());
            
            // 查询包含相同标签的文章
            LambdaQueryWrapper<ArticleTag> articleTagWrapper = new LambdaQueryWrapper<>();
            articleTagWrapper.in(ArticleTag::getTagId, tagIds)
                    .ne(ArticleTag::getArticleId, articleId);
            List<ArticleTag> relatedArticleTags = articleTagMapper.selectList(articleTagWrapper);
            
            if (!relatedArticleTags.isEmpty()) {
                // 获取文章ID列表
                List<String> relatedArticleIds = relatedArticleTags.stream()
                        .map(ArticleTag::getArticleId)
                        .collect(Collectors.toList());
                
                // 查询文章
                LambdaQueryWrapper<Article> articleWrapper = new LambdaQueryWrapper<>();
                articleWrapper.in(Article::getId, relatedArticleIds)
                        .eq(Article::getStatus, 1) // 已发布的文章
                        .orderByDesc(Article::getCreateTime)
                        .last("LIMIT 5");
                sameTagArticles = list(articleWrapper);
            }
        }
        
        // 合并文章列表并去重
        Set<Article> relatedArticles = new HashSet<>();
        relatedArticles.addAll(sameCategoryArticles);
        relatedArticles.addAll(sameTagArticles);
        
        // 转换为VO对象
        return relatedArticles.stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
    }

    private ArticleVO convertToVO(Article article) {
        ArticleVO articleVO = new ArticleVO();
        BeanUtils.copyProperties(article, articleVO);
        return articleVO;
    }

    @Override
    public Long getArticleCountByUserId() {
        String userId = SecurityUtils.getCurrentUserId();
        if (StringUtils.isBlank(userId)) {
            return 0L;
        }
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Article::getUserId, userId)
                   .eq(Article::getStatus, 1);
        return count(queryWrapper);
    }

    @Override
    public Long countView() {
        String userId = SecurityUtils.getCurrentUserId();
        if (StringUtils.isBlank(userId)) {
            return 0L;
        }
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Article::getUserId, userId)
                   .eq(Article::getStatus, 1);
        // 统计文章总浏览量
        LambdaQueryWrapper<Article> select = queryWrapper.select(Article::getViewCount);
        return count(select);
    }

    @Transactional
    @DataCacheEvict(prefix = CACHE_ARTICLE_PREFIX, key = "#id")
    public boolean removeById(String id) {
        String currentUserId = SecurityUtils.getCurrentUserId();
         if (currentUserId == null) {
             throw new RuntimeException("User not authenticated");
        }
        
        // Check ownership or admin role before deleting
        Article existingArticle = this.getById(id);
        if (existingArticle == null) {
             log.warn("Attempted to delete non-existent article with ID: {}", id);
             return false; // Or throw NotFoundException
        }
        if (!existingArticle.getUserId().equals(currentUserId) /* && !currentUserHasAdminRole() */) { // Add admin check if needed
            throw new RuntimeException("User does not have permission to delete this article");
        }

        log.info("删除文章 ID: {}", id);
        // 清除文章标签关联
        LambdaQueryWrapper<ArticleTag> tagQueryWrapper = new LambdaQueryWrapper<>();
        tagQueryWrapper.eq(ArticleTag::getArticleId, id);
        articleTagMapper.delete(tagQueryWrapper);
        
        // 删除文章
        return super.removeById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @DataCacheEvict(prefix = CACHE_ARTICLE_PREFIX, key = "#id")
    public Boolean publishArticle(String id) {
        log.info("发布文章: {}", id);

        Article article = getById(id);
        if (article == null) {
            throw new RuntimeException("文章不存在");
        }

        // 检查权限
        String currentUserId = SecurityUtils.getCurrentUserId();
        if (!article.getUserId().equals(currentUserId)) {
            throw new RuntimeException("无权限发布此文章");
        }

        article.setStatus(1); // 1表示已发布

        boolean updated = updateById(article);
        if (updated) {
            log.info("用户 {} 发布了文章 {}", currentUserId, article.getTitle());
        }

        return updated;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @DataCacheEvict(prefix = CACHE_ARTICLE_PREFIX, key = "#id")
    public Boolean unpublishArticle(String id) {
        log.info("下架文章: {}", id);

        Article article = getById(id);
        if (article == null) {
            throw new RuntimeException("文章不存在");
        }

        // 检查权限
        String currentUserId = SecurityUtils.getCurrentUserId();
        if (!article.getUserId().equals(currentUserId)) {
            throw new RuntimeException("无权限下架此文章");
        }

        article.setStatus(0); // 0表示草稿/下架

        boolean updated = updateById(article);
        if (updated) {
            log.info("用户 {} 下架了文章 {}", currentUserId, article.getTitle());
        }

        return updated;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @DataCacheEvict(prefix = CACHE_ARTICLE_PREFIX, key = "#id")
    public Boolean updateArticleStatus(String id, String status) {
        log.info("更新文章状态: {} -> {}", id, status);

        Article article = getById(id);
        if (article == null) {
            throw new RuntimeException("文章不存在");
        }

        // 检查权限
        String currentUserId = SecurityUtils.getCurrentUserId();
        if (!article.getUserId().equals(currentUserId)) {
            throw new RuntimeException("无权限修改此文章状态");
        }

        // 根据状态字符串设置状态值
        Integer statusValue = switch (status.toUpperCase()) {
            case "DRAFT" -> 0;
            case "PUBLISHED" -> 1;
            case "REVIEWING" -> 2;
            case "REJECTED" -> 3;
            default -> throw new RuntimeException("无效的状态值: " + status);
        };

        article.setStatus(statusValue);

        boolean updated = updateById(article);
        if (updated) {
            log.info("用户 {} 更新了文章 {} 的状态为 {}", currentUserId, article.getTitle(), status);
        }

        return updated;
    }
}
